package com.utf999.qiangmanger.service.impl.redis;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import com.utf999.qiangmanger.exception.BizException;
import com.utf999.qiangmanger.exception.ResponseCode;
import com.utf999.qiangmanger.service.iface.redis.IRedisCacheService;
import com.utf999.qiangmanger.service.iface.redis.IRedisOnlineUserService;
/**
 * @author yinwenjie
 */
@Component("RedisOnlineUserServiceImpl")
public class RedisOnlineUserServiceImpl implements IRedisOnlineUserService {
	/**
	 * 
	 */
	@Autowired
	private IRedisCacheService<String, String> redisCacheService;
	
	/* (non-Javadoc)
	 * @see com.ruimi.laolaibao.usercenter.service.iface.redis.RedisOnlineUserService#registIMOnlineUser(java.lang.String, java.lang.String, java.lang.String)
	 */
	@Override
	public boolean registUser(String username, String device, String token) throws BizException {
		/*
		 * 将已经注册成功的username、device和token之间的关系存放到redis中。
		 * 如果已经有了这个username、device的映射信息，那么之前的映射信息将被覆盖。
		 * 包括了三组关系：
		 * 1、username->hash(device,token)
		 * 2、token->value(username + device)
		 * 3、onlineUserNumber->set(username)
		 * */
		if(StringUtils.isEmpty(username) || StringUtils.isEmpty(device)
			|| StringUtils.isEmpty(token)) {
			throw new BizException("错误的参数信息，请检查", ResponseCode.E401);
		}
		
		RedisTemplate<String, String> redisTemplate =  this.redisCacheService.getRedisTemplate();
		return redisTemplate.execute(new MappingUserInfo(username, device, token));
	}

	/* (non-Javadoc)
	 * @see com.ruimi.laolaibao.usercenter.service.iface.redis.RedisOnlineUserService#getUserinfoByToken(java.lang.String)
	 */
	@Override
	public String getUserinfoByToken(String token) throws BizException {
		if(StringUtils.isEmpty(token)) {
			throw new BizException("错误的参数信息，请检查", ResponseCode.E401);
		}
		
		RedisTemplate<String, String> redisTemplate =  this.redisCacheService.getRedisTemplate();
		return redisTemplate.execute(new GetUserinfoByToken(token));
	}

	/* (non-Javadoc)
	 * @see com.ruimi.laolaibao.usercenter.service.iface.redis.RedisOnlineUserService#getTokenByUserinfo(java.lang.String, java.lang.String)
	 */
	@Override
	public String getTokenByUserinfo(String username, String device) throws BizException {
		//这个信息从hash映射中取，即从username->hash(device,token)中取得
		if(StringUtils.isEmpty(username) || StringUtils.isEmpty(device)) {
			throw new BizException("错误的参数信息，请检查", ResponseCode.E401);
		}
		
		RedisTemplate<String, String> redisTemplate =  this.redisCacheService.getRedisTemplate();
		return redisTemplate.execute(new GetTokenByUserinfo(username , device));
	}

	/* (non-Javadoc)
	 * @see com.ruimi.laolaibao.usercenter.service.iface.redis.RedisOnlineUserService#isUserOnline(java.lang.String, java.lang.String)
	 */
	@Override
	public boolean isUserOnline(String username, String device) throws BizException {
		/*
		 * 如果发现了username的device下有token信息，即表示当前用户已经在线
		 * */
		
		//这个信息从hash映射中取，即从username->hash(device,token)中取得
		if(StringUtils.isEmpty(username) || StringUtils.isEmpty(device)) {
			throw new BizException("错误的参数信息，请检查", ResponseCode.E401);
		}
		
		RedisTemplate<String, String> redisTemplate =  this.redisCacheService.getRedisTemplate();
		return !StringUtils.isEmpty(redisTemplate.execute(new GetTokenByUserinfo(username , device)));
	}

	/* (non-Javadoc)
	 * @see com.ruimi.laolaibao.usercenter.service.iface.redis.RedisOnlineUserService#unRegistUser(java.lang.String, java.lang.String)
	 */
	@Override
	public boolean unRegistUser(String username, String device) throws BizException {
		/*
		 * 注销用户的在线信息，既是要依次去掉username、device、token的三种关联关系
		 * */
		if(StringUtils.isEmpty(username) || StringUtils.isEmpty(device)) {
			throw new BizException("错误的参数信息，请检查", ResponseCode.E401);
		}
		//首先找到可能的token信息
		String token = this.getTokenByUserinfo(username, device);
		if(StringUtils.isEmpty(token)) {
			throw new BizException("没有发现username和device对应的token信息", ResponseCode.E401);
		}
		
		
		RedisTemplate<String, String> redisTemplate =  this.redisCacheService.getRedisTemplate();
		return redisTemplate.execute(new UNRegistUser(username , device , token));
	}

	/* (non-Javadoc)
	 * @see com.ruimi.laolaibao.usercenter.service.iface.redis.RedisOnlineUserService#unRegistUser(java.lang.String)
	 */
	@Override
	public boolean unRegistUser(String token) throws BizException {
		/*
		 * 该方法和上面的方法类似，只是首先要找到token对应的username和device信息
		 * */
		if(StringUtils.isEmpty(token)) {
			throw new BizException("错误的参数信息，请检查", ResponseCode.E401);
		}
		String userinfo = this.getUserinfoByToken(token);
		if(StringUtils.isEmpty(userinfo)) {
			throw new BizException("没有发现token对应的用户信息请检查", ResponseCode.E401);
		}
		
		String userinfoArray[] = userinfo.split(",");
		RedisTemplate<String, String> redisTemplate =  this.redisCacheService.getRedisTemplate();
		return redisTemplate.execute(new UNRegistUser(userinfoArray[0] , userinfoArray[1] , token));
	}
}

/**
 * @author yinwenjie
 */
class MappingUserInfo implements RedisCallback<Boolean>  {
	
	private String username , device , token;

	/**
	 * @param username
	 * @param device
	 * @param token
	 */
	public MappingUserInfo(String username , String device , String token) {
		this.username = username;
		this.device = device;
		this.token = token;
	}
	
	/* (non-Javadoc)
	 * @see org.springframework.data.redis.core.RedisCallback#doInRedis(org.springframework.data.redis.connection.RedisConnection)
	 */
	@Override
	public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
		//首先是username -> hash(device , token)
		connection.hSet(this.username.getBytes(), this.device.getBytes(), this.token.getBytes());
		//然后是token->username + device
		connection.append(this.token.getBytes(), (this.username + "," + this.device).getBytes());
		//最后是onlinenumber的信息
		connection.sAdd("onlineUserNumber".getBytes(), this.username.getBytes());
		
		return true;
	}
}


/**
 * 通过token获取用户名、设备信息的操作
 * @author yinwenjie
 */
class GetUserinfoByToken implements RedisCallback<String> {
	
	private String token;
	
	public GetUserinfoByToken(String token) {
		this.token = token;
	}

	/**
	 * @param connection
	 * @return
	 * @throws DataAccessException
	 */
	@Override
	public String doInRedis(RedisConnection connection) throws DataAccessException {
		byte[] userinfoBytes = connection.get(this.token.getBytes());
		if(userinfoBytes == null) {
			return null;
		}
		
		return new String(userinfoBytes);
	}
	
}

/**
 * 根据一个用户的用户名和设备信息，获取当前对应的token信息
 * @author yinwenjie
 */
class GetTokenByUserinfo implements RedisCallback<String> {
	
	private String username , device;
	
	/**
	 * @param username
	 * @param device
	 */
	public GetTokenByUserinfo(String username , String device) {
		this.username = username;
		this.device = device;
	}

	@Override
	public String doInRedis(RedisConnection connection) throws DataAccessException {
		byte[] tokenBytes = connection.hGet(this.username.getBytes(), this.device.getBytes());
		if(tokenBytes == null) {
			return null;
		}
		
		return new String(tokenBytes);
	}
}

/**
 * @author yinwenjie
 */
class UNRegistUser implements RedisCallback<Boolean> {
	
	private String username , device , token;

	/**
	 * @param username
	 * @param device
	 * @param token
	 */
	public UNRegistUser(String username , String device , String token) {
		this.username = username;
		this.device = device;
		this.token = token;
	}
	
	/* (non-Javadoc)
	 * @see org.springframework.data.redis.core.RedisCallback#doInRedis(org.springframework.data.redis.connection.RedisConnection)
	 */
	@Override
	public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
		//首先去掉username -> hash(device , token)
		connection.hDel(this.username.getBytes(), this.device.getBytes());
		//然后去掉token->username + device
		connection.del(this.token.getBytes());
		//最后去掉onlinenumber的信息
		connection.sRem("onlineUserNumber".getBytes(), this.username.getBytes());
		
		return true;
	}
	
}